package top.btcmarket.gateway.config;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerMapping;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

import io.jsonwebtoken.ExpiredJwtException;
import top.btcmarket.common.constant.CommonConst;
import top.btcmarket.common.utils.JwtToken;
/**
 * 继承抽象类zuulFilter，有4个方法需要实现：
filterType:过滤器类型，决定了过滤器在哪个周期生效。类型有pre、route、post、error，对应Spring AOP里的前加强、前后加强、后加强、异常处理。
filterOrder:过滤器的执行顺序，多个过滤器同时存在时根据这个order来决定先后顺序，越小优先级越高
shouldFilter:过滤器是否被执行，只有true时才会执行run()里的代码。我们这里除开访问163会放行其他情况都需要进行过滤判断，在生产环境一般是要根据函数条件来判断的。
run:过滤逻辑。
整个代码的zuul的代码逻辑很简单，对外统一的访问路径是zuul服务的地址，如果直接访问163过滤器不生效直接放行，访问其它内部服务结点需要判断是否有accessToken。

 * @ClassName:  AccessFilter   
 * @Description:TODO(这里用一句话描述这个类的作用)   
 * @author: pengwei
 * @date:   2017年11月15日 上午9:41:29   
 *
 */
@Component
public class AccessFilter extends ZuulFilter {

	private static Logger log = LoggerFactory.getLogger(AccessFilter.class);  
	
	@Autowired
	private RedisTokenManager tokenManager ;
	  
    @Override  
    public String filterType() {  
        //前置过滤器  
        return FilterConstants.PRE_TYPE;
    }  
  
    @Override  
    public int filterOrder() {  
        //优先级，数字越大，优先级越低  
        return 0;  
    }  
  
    @Override  
    public boolean shouldFilter() {  
        //是否执行该过滤器，true代表需要过滤  
    	
    	/*RequestContext ctx = RequestContext.getCurrentContext();  
        HttpServletRequest request = ctx.getRequest(); 
        System.out.println("---------------------request.getRequestURI():"+request.getRequestURI());
        
        if(request.getRequestURI().indexOf("/auth/token")>=0 || request.getRequestURI().indexOf("/api/security")>=0) {
            return false;  
        }else {  
            return true;  
        }   */
    	 return false;
        
    }  
  
    @Override  
    public Object run() {  
        RequestContext ctx = RequestContext.getCurrentContext();  
        HttpServletRequest request = ctx.getRequest();  
  
        log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());  
        
        // 通过 token 从redis 里面 找到  对应的  uri 权限，和 method ， 进行匹配校验
		 String pattern = (String)request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);  
		 String method = request.getMethod();
		 System.out.println("AccessFilter,pattern+---------"+pattern+",method:"+method);
		 
		 Cookie[] cookies = request.getCookies();
		 if(cookies != null && cookies.length>0){
			 for (Cookie cookie : cookies) {
					System.out.println("---------AccessFilter--------"+cookie.getName()+"--------"+cookie.getValue());
				}
		 }
		 
		
		
		 
        //获取传来的参数accessToken  
        String accessToken = request.getParameter("token"); 
        
        
        if(accessToken == null) {  
            log.warn("access token is empty");  
            
            
            //过滤该请求，不往下级服务去转发请求，到此结束  
            ctx.setSendZuulResponse(false);  
            ctx.setResponseStatusCode(401);  
            ctx.setResponseBody("{\"result\":\"accessToken为空!\"}");  
            ctx.getResponse().setContentType("text/html;charset=UTF-8");  
            return null;  
            
            
        }else{
            //TODO 根据token获取相应的登录信息，进行校验（略）
        	System.out.println(accessToken);
        	
        	try{
        		// 如果时间过期了 会报异常 
        		JwtToken.parseToken(accessToken);
//        		tokenManager.cacheToken(accessToken);
        	}catch(ExpiredJwtException e){
        		System.out.println("token 过期，继续，进一步 redis 判断");
        	}catch(Exception e){
        		System.out.println(e.getMessage());
        		ctx.setSendZuulResponse(false);  
                ctx.setResponseStatusCode(401);  
                ctx.setResponseBody("{\"result\":\"非法的 token !\"}");  
                ctx.getResponse().setContentType("text/html;charset=UTF-8");  
                return null;  
        	}
        	
        	// 过期时间 增加 redis 来判断 , 主要考虑到 增加 过期时间的问题
        	// 考虑注销的问题
    		if(!tokenManager.checkToken(accessToken)){
    			
    			String zuul = request.getHeader(CommonConst.TOKEN_ZUUL_ID);
    			
    			System.out.println("-------zuul------"+zuul);
    			
    			if(StringUtils.isNotEmpty(zuul) && zuul.equals(accessToken)){
    				//防止一次 流程没有 走完，token 被注销了
    				System.out.println("#########内部验证通过，继续##############");
    				
    			}else{
    				
    				System.out.println("----redis 也过期了，没救了 ---------");
    				
    				ctx.setSendZuulResponse(false);  
                    ctx.setResponseStatusCode(401);  
                    ctx.setResponseBody("{\"result\":\"token 过期  或者注销了 !\"}");  
                    ctx.getResponse().setContentType("text/html;charset=UTF-8");  
                    return null;  
    			}
    			
    			
    		}else{
    			System.out.println("token 正常");
        		ctx.setSendZuulResponse(true); 
    		}
        	
        	
        	/*if(JwtToken.isTokenExpired(String.valueOf(accessToken))){
        		System.out.println("token 过期");
        		
    			ctx.setSendZuulResponse(false);  
                ctx.setResponseStatusCode(401);  
                ctx.setResponseBody("{\"result\":\"token 过期  或者注销了 !\"}");  
                ctx.getResponse().setContentType("text/html;charset=UTF-8");  
                return null;  
        		
                
        	}else{
        		System.out.println("token 没有 过期");
        		ctx.setSendZuulResponse(true);  
        	}*/
        	
        }  
        
        
        
        //如果有token，则进行路由转发  
        
        //并且生成zuul 内部转发 ID， 避免 在服务之间 还没有调用的完成 时候 token过期或者被注销
        ctx.addZuulRequestHeader(CommonConst.TOKEN_ZUUL_ID,accessToken);
        
        
        log.info("access token ok");  
        //这里return的值没有意义，zuul框架没有使用该返回值  
        return null;  
    }  

}
